﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using ICSharpCode.WinFormsUI.Forms;

namespace ICSharpCode.WinFormsUI.Core
{
    public partial class HtmlTextBox : Control
    {        
        private int renderPos = 0;
        private int scrollBarXPos = 0;
        
        private List<TitleItem> titles = null;
        private List<ClickItem> clicks = null;
  
        private int[] margin = new int[4];
        private int[] padding = new int[4];

        ToolTip tooltip = new ToolTip();

        private List<ICSharpCode.WinFormsUI.Core.HtmlTextParse.StringItem> items = null;

        private System.Windows.Forms.HScrollBar VScrollBar = new System.Windows.Forms.HScrollBar();

        public HtmlTextBox()
        {
            InitializeComponent();
            InitializeControls();
        }

        private void InitializeControls()
        {
            margin[2] = margin[0] = 0;
            padding[2] = padding[0] = 16;           

            VScrollBar.Value = 0;
            VScrollBar.Visible = false;          
            VScrollBar.Dock = DockStyle.Bottom;
            //VScrollBar.Orientation = ScrollBarOrientation.Horizontal;
            VScrollBar.Scroll += VScrollBar_Scroll;
            this.DoubleBuffered = true;

            this.Controls.Add(VScrollBar);           
        }

        private ICSharpCode.WinFormsUI.Core.HtmlTextParse.StringCollection _documentText;
        public ICSharpCode.WinFormsUI.Core.HtmlTextParse.StringCollection DocumentText
        {
            get { return _documentText; }
            set
            {
                _documentText = value;
                if (_documentText != null)
                {
                    items = new List<HtmlTextParse.StringItem>();
                    int cutIndex = 0;
                    string HtmlText = DocumentText.Text;
                    foreach (ICSharpCode.WinFormsUI.Core.HtmlTextParse.StringItem item in DocumentText)
                    {
                        string word = HtmlText.Substring(0, item.StringIndex - cutIndex);
                        string keyword = HtmlText.Substring(item.StringIndex - cutIndex, item.StringLength);

                        if (!string.IsNullOrEmpty(word))
                            items.Add(item.New(word));
                        items.Add(item.Clone());

                        HtmlText = HtmlText.Substring(item.StringIndex - cutIndex + item.StringLength);
                        //cutIndex += (cutIndex == 0 ? item.StringIndex : 0) + item.StringLength;
                        cutIndex = item.StringIndex + item.StringLength;

                        if (!string.IsNullOrEmpty(HtmlText)
                            && HtmlText.IndexOf("</font>") == -1)
                        {
                            items.Add(item.New(HtmlText));
                        }
                    }

                    if (this.DocumentText.Count == 0)
                        items.Add(new ICSharpCode.WinFormsUI.Core.HtmlTextParse.StringItem(this.DocumentText.Text));

                    MeasureVScrollBar();
                }

                this.Invalidate();

            }
        }

        private Bitmap _documentIcon;
        public Bitmap DocumentIcon
        {
            get { return _documentIcon; }
            set
            {
                _documentIcon = value;
                this.Invalidate();
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            OnControlRender(e.Graphics);
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            this.Invalidate();
            MeasureVScrollBar();
            ResetVScrollBar();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (this.titles != null && this.titles.Count > 0)
            {
                bool changeCusor = false;
                string titleText = string.Empty;
                if (e.Location.X > 2 * padding[0] + DocumentIcon.Width)
                {
                    foreach (TitleItem title in this.titles)
                    {
                        if (title.Bound.Contains(e.Location))
                        {
                            titleText = title.Title;
                            changeCusor = true;
                            break;
                        }
                    }
                }
                

                if (changeCusor)
                {
                    if (this.Cursor != Cursors.Hand)
                    {
                        this.Cursor = Cursors.Hand;
                        tooltip.SetToolTip(this, titleText);
                    }
                }
                else
                {
                    if (this.Cursor != Cursors.Default)
                        this.Cursor = Cursors.Default;
                    tooltip.RemoveAll();
                }
                
            }
            else
            {
                if (this.Cursor != Cursors.Default)
                    this.Cursor = Cursors.Default;
                tooltip.RemoveAll();
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);

            if (this.clicks != null && this.clicks.Count > 0)
            {
                bool clickenable = false;
                ClickItem item = null;

                if (e.Location.X > 2 * padding[0] + this.DocumentIcon.Width)
                {
                    foreach (ClickItem click in this.clicks)
                    {
                        if (click.Bound.Contains(e.Location))
                        {
                            item = click;
                            clickenable = true;
                            break;
                        }
                    }
                }

                if (clickenable && item != null)
                {
                    string[] argments = Trim(item.Argments.Split(','));
                    Type type = ((MessageForm)this.Parent).ReflectionType;
                    if (type == null)
                        return;
                    System.Reflection.MethodInfo methodInfo = type.GetMethod(item.EventName);// type.GetMethod(item.EventName, BindingFlags.Default);
                    if (methodInfo != null)
                    {
                        methodInfo.Invoke(((MessageForm)this.Parent).ReflectionSender, argments);
                    }
                }

            }

        }

        protected void OnControlRender(Graphics graphic)
        {
            DocumentMarginCalc(graphic);
            DrawDocumentText(graphic);
            DrawDocumentIcon(graphic);
            DrawDocumentBorder(graphic);
        }

        private void DrawDocumentBorder(Graphics graphic)
        {
            Rectangle rect = new Rectangle(0, 0, Width, Height);
            //if (this.VScrollBar.Visible)
            //{
            //    rect.Height -= this.VScrollBar.Height;
            //}
            rect.Inflate(-padding[0] / 2, -padding[0] / 2);
            graphic.DrawRectangle(new Pen(this.BackColor, padding[0]), rect);
        }

        protected void DrawDocumentIcon(Graphics graphic)
        {
            if (this.DocumentIcon != null)
            {
                Rectangle iconRect = new Rectangle(padding[0] + margin[0], (this.Height - this.DocumentIcon.Height - padding[1] - padding[3]) / 2, this.DocumentIcon.Width, this.DocumentIcon.Height);
                graphic.FillRectangle(new SolidBrush(this.BackColor), new Rectangle(0, 0, 2 * padding[0] + this.DocumentIcon.Width, this.Height));
                graphic.DrawImage(this.DocumentIcon, iconRect);
            }
        }

        protected void DrawDocumentText(Graphics graphic)
        {
            if (this.items != null)
            {
                clicks = new List<ClickItem>();
                titles = new List<TitleItem>();
                Rectangle textRect = new Rectangle(2 * padding[0] + (this.DocumentIcon == null ? 0 : this.DocumentIcon.Width), (this.Height - 2 * 16) / 2, this.Width - 2 * padding[0] - padding[2] - (this.DocumentIcon == null ? 0 : this.DocumentIcon.Width), 2 * 16);
                int drawPos = renderPos = margin[0];
                foreach (ICSharpCode.WinFormsUI.Core.HtmlTextParse.StringItem item in this.items)
                {
                    Font drawFont = this.Font;
                    Color drawColor=Color.Black ;
                    Dictionary<string,string> attrs = item.Attrs;
                    if (attrs != null)
                    {
                        foreach (string key in attrs.Keys)
                        {
                            switch (key)
                            {
                                case "color":
                                    drawColor = ColorTransfrom(attrs[key]);
                                    break;
                                case "size":
                                    drawFont = new Font(drawFont.FontFamily, float.Parse(attrs[key]), drawFont.Style);
                                    break;
                                case "name":
                                    drawFont = new Font(attrs[key], drawFont.Size, drawFont.Style);
                                    break;
                                case "style":
                                    drawFont = new Font(drawFont.FontFamily, drawFont.Size, StyleTransfrom(attrs[key]));
                                    break;
                            }
                        }
                    }                   
                    Size drawSize = TextRenderer.MeasureText(graphic, item.Text, drawFont, textRect.Size);
                    Rectangle drawRect = new Rectangle(textRect.X + drawPos + scrollBarXPos, textRect.Y, drawSize.Width, textRect.Height);
                    TextRenderer.DrawText(graphic, item.Text, drawFont,drawRect, drawColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);

                    if (attrs != null && attrs.ContainsKey("title"))
                    {
                        string tooltip = (attrs["title"] + "").Replace("\\s"," ");
                        if (!string.IsNullOrEmpty(tooltip))
                        {                          
                            titles.Add(new TitleItem() {
                                Title = tooltip,
                                Bound = drawRect
                            });
                        }
                    }

                    if (attrs != null && attrs.ContainsKey("onclick"))
                    {
                        string clickevent = (attrs["onclick"] + "").Replace("\\s", " ");
                        if (!string.IsNullOrEmpty(clickevent))
                        {
                            string method = clickevent.Substring(0, clickevent.IndexOf("("));
                            string argments = clickevent.Substring(clickevent.IndexOf("(") + 1, clickevent.IndexOf(")") - clickevent.IndexOf("(") - 1);
                            clicks.Add(new ClickItem() {
                                EventName = method,
                                Argments = argments,
                                Bound = drawRect
                            });
                        }
                    }

                    drawPos += drawSize.Width;
                    renderPos = drawPos + drawSize.Width;

                }

                MeasureVScrollBar();

            }
        }

        protected Size MeasureDrawViewSize(Graphics graphic)
        {
            int view_width = 0;
            int view_height = 0;

            if (this.DocumentIcon != null)
            {
                view_width = this.DocumentIcon.Width;
                view_height = this.DocumentIcon.Height;
            }

            if (this.items != null)
            {
                foreach (ICSharpCode.WinFormsUI.Core.HtmlTextParse.StringItem item in this.items)
                {
                    Font drawFont = this.Font;
                    Color drawColor = Color.Black;
                    Dictionary<string, string> attrs = item.Attrs;
                    if (attrs != null)
                    {
                        foreach (string key in attrs.Keys)
                        {
                            switch (key)
                            {
                                case "color":
                                    drawColor = ColorTransfrom(attrs[key]);
                                    break;
                                case "size":
                                    drawFont = new Font(drawFont.FontFamily, float.Parse(attrs[key]), drawFont.Style);
                                    break;
                                case "name":
                                    drawFont = new Font(attrs[key], drawFont.Size, drawFont.Style);
                                    break;
                                case "style":
                                    drawFont = new Font(drawFont.FontFamily, drawFont.Size, StyleTransfrom(attrs[key]));
                                    break;
                            }
                        }
                    }
                    Size drawSize = TextRenderer.MeasureText(graphic, item.Text, drawFont);
                    view_width += drawSize.Width;
                }
            }          

            return new Size(view_width, view_height);
        }

        protected void DocumentMarginCalc(Graphics graphic)
        {
            Size viewSize = MeasureDrawViewSize(graphic);
            if (this.DocumentIcon != null &&
                    viewSize.Width < this.Width - this.DocumentIcon.Width - 2 * padding[0])
            {
                margin[0] = margin[2] = (this.Width - 2 * padding[0] - viewSize.Width) / 2;
            }
            else
            {
                margin[0] = margin[2] = 0;
            }
        }

        private void VScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            scrollBarXPos += -(e.NewValue - e.OldValue);
            this.Invalidate();
        }

        protected void MeasureVScrollBar()
        {
            if (renderPos > 0)
            {
                VScrollBar.Minimum = 0;
                if (renderPos > this.Width)
                {
                    VScrollBar.Visible = true;
                    VScrollBar.Maximum = renderPos - this.Width + 10 + 2 * padding[0];
                }
                else
                {
                    VScrollBar.Visible = false;
                    VScrollBar.Maximum = 100;
                }                
            }           

        }

        protected void ResetVScrollBar()
        {
            scrollBarXPos = 0;
            VScrollBar.Value = 0;
        }

        protected Color ColorTransfrom(string name)
        {
            Color result = Color.Black;
            switch (name)
            {
                case "red":
                    result = Color.Red;
                    break;
                case "yellow":
                    result = Color.Yellow;
                    break;
                case "blue":
                    result = Color.Blue;
                    break;
            }
            return result;
        }

        protected FontStyle StyleTransfrom(string name)
        {
            FontStyle result = FontStyle.Regular;
            name = string.IsNullOrEmpty(name) ? "" : name.ToLower();
            if (name.IndexOf("|") == -1)
            {
                switch (name)
                {
                    case "bold":
                        result = FontStyle.Bold;
                        break;
                    case "italic":
                        result = FontStyle.Italic;
                        break;
                    case "underline":
                        result = FontStyle.Underline;
                        break;
                }
            }
            else
            {
                if (name.Contains("italic"))
                {
                    result = result | FontStyle.Italic;
                }
                if (name.Contains("bold"))
                {
                    result = result | FontStyle.Bold;
                }
                if (name.Contains("underline"))
                {
                    result = result | FontStyle.Underline;
                }
            }
            return result;
        }

        protected string[] Trim(string[] input)
        {
            if (input != null && input.Length > 0)
            {
                for (int i = 0; i < input.Length; i++)
                {
                    input[i] = input[i].Trim('"');
                }
            }
            return input;
        }

        private class TitleItem
        {
            public System.Drawing.Rectangle Bound { get; set; }
            public string Title { get; set; }
        }

        public class ClickItem
        {
            public string EventName { get; set; }
            public string Argments { get; set; }
            public System.Drawing.Rectangle Bound { get; set; }

        }

    }
}
